// $URL: http://mingw-lib.googlecode.com/svn/trunk/working/avcodec_to_widget_8/t_box_player.h $
// $Rev: 298 $
// $Author: akio.miyoshi $
// $Date:: 2010-07-28 14:12:27 +0000#$

#ifndef T_BOX_PLAYER_H
#define T_BOX_PLAYER_H

#include "stable.h"
#include "t_av_packet.h"
#include "t_generator.h"
#include "t_box_input.h"

class T_VideoProgressBar2 : public QSlider
{
    Q_OBJECT
public:
    explicit T_VideoProgressBar2(QWidget *parent = 0)
        : QSlider(parent)
        , m_seek_move(-1)
    {
        this->setOrientation(Qt::Horizontal);
        this->setTracking(false);
        QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
        sizePolicy.setHorizontalStretch(0);
        sizePolicy.setVerticalStretch(0);
        sizePolicy.setHeightForWidth(this->sizePolicy().hasHeightForWidth());
        this->setSizePolicy(sizePolicy);
        connect(this, SIGNAL(sliderPressed()), this, SLOT(on_this_sliderPressed()));
        connect(this, SIGNAL(sliderReleased()), this, SLOT(on_this_sliderReleased()));
        connect(this, SIGNAL(sliderMoved(int)), this, SLOT(on_this_sliderMoved(int)));
        connect(this, SIGNAL(valueChanged(int)), this, SLOT(on_this_valueChanged(int)));
    }
    bool seekRequest()
    {
        QMutexLocker v_locker(&m_seek_queue_mutex);
        Q_UNUSED(v_locker);
        return (m_seek_queue.size()>0);
    }
    void seekRequest(bool b)
    {
        //m_seek_request = b;
    }
    int seekTarget()
    {
        QMutexLocker v_locker(&m_seek_queue_mutex);
        Q_UNUSED(v_locker);
        if(m_seek_queue.size()>0)
        {
            int v_target = m_seek_queue.last();
            m_seek_queue.clear();
            return v_target;
        }
        return -1;
    }
    T_SlimpleWaitCondition *seekWaitCondition()
    {
        return &m_seek_wait_cond;
    }
public slots:
    void setTiming(int a_msec)
    {
        if(this->isSliderDown()) return;
        if(seekRequest()) return;
        this->setValue(a_msec);
    }
protected slots:
    void on_this_sliderPressed()
    {
        qDebug() << this->minimum() << this->maximum() << this->value() << this->sliderPosition();
        //this->setTracking(false);
        //m_seek_target = value();
        //m_seek_request = true;
    }
    void on_this_sliderReleased()
    {
        qDebug() << "[on_this_sliderReleased]" << this->sliderPosition();
        p_set_one(sliderPosition());
    }
    void on_this_sliderMoved(int a_value)
    {
        // void QAbstractSlider::sliderMoved ( int value )   [signal]
        // This signal is emitted even when tracking is turned off.
        //QMutexLocker v_locker(&m_seek_queue_mutex);
        //Q_UNUSED(v_locker);
        qDebug() << "[on_this_sliderMoved]" << this->sliderPosition();
        if(qAbs(a_value-m_seek_move)<5000)
        {
            return;
        }
        m_seek_move = a_value;
        p_enqueue(sliderPosition());
    }
    void on_this_valueChanged(int a_value)
    {
        //m_seek_target = a_value;
        //m_seek_request = true;
    }
protected:
    void p_enqueue(int a_value)
    {
        QMutexLocker v_locker(&m_seek_queue_mutex);
        Q_UNUSED(v_locker);
        m_seek_queue.enqueue(a_value);
    }
    void p_set_one(int a_value)
    {
        QMutexLocker v_locker(&m_seek_queue_mutex);
        Q_UNUSED(v_locker);
        m_seek_queue.clear();
        m_seek_queue.enqueue(a_value);
    }
protected:
    T_SlimpleWaitCondition m_seek_wait_cond;
    //QMutex m_seek_wait_mutex;
    //QWaitCondition m_seek_wait_cond;
    T_RecursiveMutex m_seek_queue_mutex;
    QQueue<int> m_seek_queue;
    int m_seek_move;
};

class T_VideoProgressBar : public QProgressBar
{
    Q_OBJECT
public:
    explicit T_VideoProgressBar(QWidget *parent = 0)
        : QProgressBar(parent)
        , m_old_x(-1)
        , m_seek_request(false)
        , m_seek_target(-1)
    {
    }
public:
    virtual QString text() const
    {
        QTime v_val_clock(0, 0, 0, 0);
        v_val_clock = v_val_clock.addMSecs(this->value());
        QTime v_max_clock(0, 0, 0, 0);
        v_max_clock = v_max_clock.addMSecs(this->maximum());
        return v_val_clock.toString("hh:mm:ss[zzz]") + "/" + v_max_clock.toString("hh:mm:ss[zzz]");
    }
    void setTiming(int a_msec)
    {
        this->setValue(a_msec);
    }
    int seekRequest()
    {
        return m_seek_request;
    }
    void seekRequest(bool b)
    {
        m_seek_request = b;
    }
    int seekTarget()
    {
        return m_seek_target;
        //return p_calc_seek_target();
    }
    T_SlimpleWaitCondition *seekWaitCondition()
    {
        return &m_seek_wait_cond;
    }

protected:
    virtual bool event(QEvent *e)
    {
        if(e->type()==QEvent::MouseButtonPress)
        {
            QMouseEvent *v_me = static_cast<QMouseEvent *>(e);
            Qt::MouseButtons v_buttons = v_me->buttons();
            if(! (v_buttons & Qt::LeftButton) )
            {
                //return true;
                return QProgressBar::event(e);
            }
            p_handle_seek_click(v_me);
            //return true;
            return QProgressBar::event(e);
        }
        if(e->type()==QEvent::MouseButtonRelease)
        {
            QMouseEvent *v_me = static_cast<QMouseEvent *>(e);
            Qt::MouseButtons v_buttons = v_me->buttons();
            if(! (v_buttons & Qt::LeftButton) ) return true;
            if(m_old_x >= 0 && qAbs(v_me->x()-m_old_x)<2)
            {
                //return true;
                return QProgressBar::event(e);
            }
            p_handle_seek_click(v_me);
            //return true;
            return QProgressBar::event(e);
        }
        if(e->type()==QEvent::MouseMove)
        {
            QMouseEvent *v_me = static_cast<QMouseEvent *>(e);
            Qt::MouseButtons v_buttons = v_me->buttons();
            if(! (v_buttons & Qt::LeftButton) ) return true;
            if(m_move_sw.elapsed()==0)
            {
                //return true;
                return QProgressBar::event(e);
            }
            int v_dist = qAbs(v_me->x()-m_old_x);
            double v_speed = 1.0 * v_dist / m_move_sw.elapsed();
            double v_speed_limit = v_speed * 40;
            if(v_speed_limit<5)
            {
                v_speed_limit = 5;
            }
            if(v_speed_limit>10)
            {
                v_speed_limit = 10;
            }
            ////qDebug() << "[v_speed]" << v_speed << v_speed_limit;
            if(m_old_x >= 0 && v_dist<v_speed_limit)
            {
                //return true;
                return QProgressBar::event(e);
            }
            m_move_sw.reset();
            ////qDebug() << "[v_dist]" << v_dist;
            p_handle_seek_click(v_me);
            //return true;
            return QProgressBar::event(e);
        }

        return QProgressBar::event(e);
    }
#if 0x0
    int p_get_cursor_x()
    {
        POINT v_pos;
        GetCursorPos( &v_pos );
        QPoint v_client_pos = this->mapFromGlobal(QPoint(v_pos.x, v_pos.y));
        return v_client_pos.x();
    }
#endif
    int p_calc_seek_target(QMouseEvent *v_me)
    {
        m_stopwatch.reset();
        int x = v_me->x();
        m_old_x = x;
        int w = this->width();
        int v_min = this->minimum();
        int v_max = this->maximum();
        int v_one = v_max - v_min;
        qint64 value = v_min + (v_one * x / w);
        return (int)value;
    }
    void p_handle_seek_click(QMouseEvent *v_me)
    {
        m_stopwatch.reset();
        int value = p_calc_seek_target(v_me);
        m_seek_target = value;
        m_seek_request = true;
        m_seek_wait_cond.wakeAll();
        emit clicked(value);
    }
protected:
    T_AutoRunStopWatch m_stopwatch;
    T_AutoRunStopWatch m_move_sw;
    int m_old_x;
    bool m_seek_request;
    int m_seek_target;
    T_SlimpleWaitCondition m_seek_wait_cond;
signals:
    void clicked(int value);
};

class T_BufferProgressBar : public QProgressBar
{
    Q_OBJECT
public:
    explicit T_BufferProgressBar(QWidget *parent = 0)
        : QProgressBar(parent)
        , m_buffering(false)
        , m_stopwatch(true)
    {
    }
public:
    virtual QString text() const
    {
        if(m_buffering)
        {
            return "Buffering..." + QProgressBar::text();
        }
        return QProgressBar::text();
    }
    void setBuffering(bool b)
    {
        m_buffering = b;
    }
    bool isBuffering()
    {
        return m_buffering;
    }
protected:
    bool m_buffering;
    T_StopWatch m_stopwatch;
};

struct T_Box_Stream_Info
{
    bool isValid;
    int stream_index;
    AVCodecContext *codec_ctx;
    AVCodec *codec;
    AVStream *stream;
    T_AV_Packet_Queue packet_queue;
    explicit T_Box_Stream_Info(T_Box_Player_Context *a_player_ctx, AVMediaType a_media_type);
};

class T_Box_Player_Context : public QObject
{
    Q_OBJECT
public:
    enum
    {
        PLAYER_HEAD_HEADGE = 2000,
        PLAYER_BUFFER_TIME = 6000,
    };
    T_Box_Input *m_box_input;
    AVFormatContext *m_format_ctx;
    T_Box_Stream_Info m_audio;
    T_Box_Stream_Info m_video;
    T_StopWatch m_stopwatch;
    QAudioOutput *m_audioOutput;
    T_Generator *m_generator;
    DECLARE_ALIGNED(16,quint8,m_audio_decode_array)[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
    DECLARE_ALIGNED(16,quint8,m_audio_convert_array)[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
    T_VideoProgressBar *v_prog;
    //T_VideoProgressBar2 *v_prog;
    T_BufferProgressBar *v_prog2;
    explicit T_Box_Player_Context(class T_Box_Input *a_box_input, AVFormatContext *a_format_ctx);
    ~T_Box_Player_Context();
    void setAudioOutput(QAudioOutput *a_audioOutput)
    {
        m_audioOutput = a_audioOutput;
    }
    QReadWriteLock *seekMutex()
    {
        return &m_seek_mutex;
    }
    T_StopWatch *seekTimer()
    {
        return &m_seek_stopwatch;
    }
    qint64 seekTarget()
    {
        return m_seek_target;
    }
    qint64 seek(qint64 a_msec);
    T_AV_Packet *readPacket();
    T_AV_Packet *enqueuePacket();
    qint64 duration()
    {
        return  ((qint64)1000) * this->m_format_ctx->duration / AV_TIME_BASE;
    }
    QString durationToString(const QString &a_format)
    {
        QTime v_duration_clock(0, 0, 0, 0);
        v_duration_clock = v_duration_clock.addMSecs(this->duration());
        return v_duration_clock.toString(a_format);
    }
    bool needBuffering()
    {
        if(m_video.packet_queue.duration()<PLAYER_BUFFER_TIME)
        {
            return true;
        }
        return false;
    }
protected:
    T_RecursiveRWLock m_seek_mutex;
    T_AutoRunStopWatch m_seek_stopwatch;
    qint64 m_seek_target;
public: //FIXME
    T_AVPicture_Resizable m_resize_pic;
};

#endif // T_BOX_PLAYER_H
